/**
 * Copyright (c) 2006-2015, JGraph Ltd
 * Copyright (c) 2006-2015, Gaudenz Alder
 */
/**
 * Class: mxPanningManager
 *
 * Implements a handler for panning.
 */
function mxPanningManager(graph) {
  this.thread = null;
  this.active = false;
  this.tdx = 0;
  this.tdy = 0;
  this.t0x = 0;
  this.t0y = 0;
  this.dx = 0;
  this.dy = 0;
  this.scrollbars = false;
  this.scrollLeft = 0;
  this.scrollTop = 0;

  this.mouseListener = {
    mouseDown: function (sender, me) {},
    mouseMove: function (sender, me) {},
    mouseUp: mxUtils.bind(this, function (sender, me) {
      if (this.active) {
        this.stop();
      }
    }),
  };

  graph.addMouseListener(this.mouseListener);

  this.mouseUpListener = mxUtils.bind(this, function () {
    if (this.active) {
      this.stop();
    }
  });

  // Stops scrolling on every mouseup anywhere in the document
  mxEvent.addListener(document, "mouseup", this.mouseUpListener);

  var createThread = mxUtils.bind(this, function () {
    this.scrollbars = mxUtils.hasScrollbars(graph.container);
    this.scrollLeft = graph.container.scrollLeft;
    this.scrollTop = graph.container.scrollTop;

    return window.setInterval(
      mxUtils.bind(this, function () {
        this.tdx -= this.dx;
        this.tdy -= this.dy;

        if (this.scrollbars) {
          var left = -graph.container.scrollLeft - Math.ceil(this.dx);
          var top = -graph.container.scrollTop - Math.ceil(this.dy);
          graph.panGraph(left, top);
          graph.panDx = this.scrollLeft - graph.container.scrollLeft;
          graph.panDy = this.scrollTop - graph.container.scrollTop;
          graph.fireEvent(new mxEventObject(mxEvent.PAN));
          // TODO: Implement graph.autoExtend
        } else {
          graph.panGraph(this.getDx(), this.getDy());
        }
      }),
      this.delay
    );
  });

  this.isActive = function () {
    return active;
  };

  this.getDx = function () {
    return Math.round(this.tdx);
  };

  this.getDy = function () {
    return Math.round(this.tdy);
  };

  this.start = function () {
    this.t0x = graph.view.translate.x;
    this.t0y = graph.view.translate.y;
    this.active = true;
  };

  this.panTo = function (x, y, w, h) {
    if (!this.active) {
      this.start();
    }

    this.scrollLeft = graph.container.scrollLeft;
    this.scrollTop = graph.container.scrollTop;

    w = w != null ? w : 0;
    h = h != null ? h : 0;

    var c = graph.container;
    this.dx = x + w - c.scrollLeft - c.clientWidth;

    if (this.dx < 0 && Math.abs(this.dx) < this.border) {
      this.dx = this.border + this.dx;
    } else if (this.handleMouseOut) {
      this.dx = Math.max(this.dx, 0);
    } else {
      this.dx = 0;
    }

    if (this.dx == 0) {
      this.dx = x - c.scrollLeft;

      if (this.dx > 0 && this.dx < this.border) {
        this.dx = this.dx - this.border;
      } else if (this.handleMouseOut) {
        this.dx = Math.min(0, this.dx);
      } else {
        this.dx = 0;
      }
    }

    this.dy = y + h - c.scrollTop - c.clientHeight;

    if (this.dy < 0 && Math.abs(this.dy) < this.border) {
      this.dy = this.border + this.dy;
    } else if (this.handleMouseOut) {
      this.dy = Math.max(this.dy, 0);
    } else {
      this.dy = 0;
    }

    if (this.dy == 0) {
      this.dy = y - c.scrollTop;

      if (this.dy > 0 && this.dy < this.border) {
        this.dy = this.dy - this.border;
      } else if (this.handleMouseOut) {
        this.dy = Math.min(0, this.dy);
      } else {
        this.dy = 0;
      }
    }

    if (this.dx != 0 || this.dy != 0) {
      this.dx *= this.damper;
      this.dy *= this.damper;

      if (this.thread == null) {
        this.thread = createThread();
      }
    } else if (this.thread != null) {
      window.clearInterval(this.thread);
      this.thread = null;
    }
  };

  this.stop = function () {
    if (this.active) {
      this.active = false;

      if (this.thread != null) {
        window.clearInterval(this.thread);
        this.thread = null;
      }

      this.tdx = 0;
      this.tdy = 0;

      if (!this.scrollbars) {
        var px = graph.panDx;
        var py = graph.panDy;

        if (px != 0 || py != 0) {
          graph.panGraph(0, 0);
          graph.view.setTranslate(
            this.t0x + px / graph.view.scale,
            this.t0y + py / graph.view.scale
          );
        }
      } else {
        graph.panDx = 0;
        graph.panDy = 0;
        graph.fireEvent(new mxEventObject(mxEvent.PAN));
      }
    }
  };

  this.destroy = function () {
    graph.removeMouseListener(this.mouseListener);
    mxEvent.removeListener(document, "mouseup", this.mouseUpListener);
  };
}

/**
 * Variable: damper
 *
 * Damper value for the panning. Default is 1/6.
 */
mxPanningManager.prototype.damper = 1 / 6;

/**
 * Variable: delay
 *
 * Delay in milliseconds for the panning. Default is 10.
 */
mxPanningManager.prototype.delay = 10;

/**
 * Variable: handleMouseOut
 *
 * Specifies if mouse events outside of the component should be handled. Default is true.
 */
mxPanningManager.prototype.handleMouseOut = true;

/**
 * Variable: border
 *
 * Border to handle automatic panning inside the component. Default is 0 (disabled).
 */
mxPanningManager.prototype.border = 0;
